﻿using System;
using Microsoft.Practices.CompositeUI.Properties;
using Microsoft.Practices.CompositeUI.Utility;

namespace Microsoft.Practices.CompositeUI.Commands
{
    /// <summary>
    /// 	Defines a command adapter class, which provides the logic needed for an object to be able 
    /// 	to invoke a <see cref = "Command" />.
    /// </summary>
    public abstract class CommandAdapter : IDisposable
    {
        #region Fields & Properties

        private Command _boundCommand;

        #endregion

        #region Events

        /// <summary>
        /// 	Event to be fired by the <see cref = "CommandAdapter" /> implementations
        /// 	to signal that the <see cref = "Command" /> should execute.
        /// </summary>
        public event EventHandler ExecuteCommand;

        /// <summary>
        /// 	Causes the execution of the <see cref = "Command" />.
        /// </summary>
        protected virtual void OnExecuteCommand()
        {
            var executeCommand = ExecuteCommand;
            if (executeCommand != null)
            {
                executeCommand(this, EventArgs.Empty);
            }
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary>
        /// 	Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 	Called to free resources.
        /// </summary>
        /// <param name = "disposing">Should be true when calling from Dispose().</param>
        protected virtual void Dispose(bool disposing)
        {
        }

        #endregion

        #region Invoker Operations

        /// <summary>
        /// 	Gets the count of invokers in the <see cref = "CommandAdapter" />.
        /// </summary>
        public abstract int InvokerCount
        {
            get;
        }

        /// <summary>
        /// 	Adds an invoker to the <see cref = "CommandAdapter" />.
        /// </summary>
        public abstract void AddInvoker(object invoker, string eventName);

        /// <summary>
        /// 	Removes the invoker from the <see cref = "CommandAdapter" />.
        /// </summary>
        public abstract void RemoveInvoker(object invoker, string eventName);

        /// <summary>
        /// 	Checks if the <see cref = "CommandAdapter" /> contains the invoker.
        /// </summary>
        /// <param name = "invoker">The invoker to check for.</param>
        /// <returns>true if the invoker is in the adapter; otherwise false.</returns>
        public abstract bool ContainsInvoker(object invoker);

        #endregion

        #region Bind & Unbind Command

        /// <summary>
        /// 	Called when the <see cref = "CommandAdapter" /> is registered with a <see cref = "Command" />.
        /// </summary>
        /// <param name = "command">The <see cref = "Command" /> the adapter is being added to.</param>
        /// <remarks>
        /// 	The default implementation of this method adds the CommandChangedHandler method
        /// 	handler to the <see cref = "Command.Changed" /> event.
        /// </remarks>
        public virtual void BindCommand(Command command)
        {
            Guard.ArgumentNotNull(command, "command");
            if (_boundCommand != null)
            {
                throw new InvalidOperationException(Resources.AdapterAlreadyBoundToACommand);
            }
            command.Changed += CommandChangedHandler;
            _boundCommand = command;
        }

        /// <summary>
        /// 	Called when <see cref = "CommandAdapter" /> is removed from a <see cref = "Command" />.
        /// </summary>
        /// <param name = "command">The <see cref = "Command" /> the adapter is being removed from.</param>
        /// <remarks>
        /// 	The default implementation of this method removes the CommandChangedHandler method handler 
        /// 	from the <see cref = "Command.Changed" /> event.
        /// </remarks>
        public virtual void UnbindCommand(Command command)
        {
            Guard.ArgumentNotNull(command, "command");
            if (_boundCommand != command)
            {
                throw new InvalidOperationException(Resources.AdapterNotBoundToGivenCommand);
            }
            command.Changed -= CommandChangedHandler;
            _boundCommand = null;
        }

        #region Event Handlers

        private void CommandChangedHandler(object sender, EventArgs e)
        {
            OnCommandChanged((Command) sender);
        }

        #endregion

        #endregion

        #region Command Changed

        /// <summary>
        /// 	Handles the <see cref = "Command.Changed" /> event.
        /// </summary>
        /// <param name = "command">The <see cref = "Command" /> that fired the Changed event.</param>
        protected virtual void OnCommandChanged(Command command)
        {
        }

        #endregion
    }
}